package cs575.fantasyclient.ui;

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Vector;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.Spring;
import javax.swing.SpringLayout;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;

import cs575.common.Player;
import cs575.fantasyclient.services.GetAllPlayers;
import cs575.fantasyclient.services.GetPlayer;

public class FantasyClient extends JFrame implements ActionListener {

	private static final long serialVersionUID = 1L;

	private JSplitPane contentPane;
	private JPanel rightPane;
	private JPanel leftPane;
	private JList playerList;
	private JScrollPane scrollPane;
	private JButton addPlayerToTeam;
	private JButton viewStats;
	private JButton getAllPlayers;
	private JButton clearTeam;
	private DefaultTableModel tModel;
	private JTable team;

	private JTextField ipAndPort;
	private JTextField teamName;

	private SpringLayout rightLayout;
	private SpringLayout leftLayout;

	private Thread notifyThread;
	private Thread teamNotifyThread;

	private static Map<String, Player> availablePlayers;
	private static Map<String, Player> currentTeam;

	public FantasyClient() {
		availablePlayers = new HashMap<String, Player>();
		currentTeam = new HashMap<String, Player>();

		rightLayout = new SpringLayout();
		leftLayout = new SpringLayout();

		leftPane = new JPanel();
		leftPane.setLayout(leftLayout);

		getAllPlayers = new JButton("Request Player List");
		getAllPlayers.setName("GETALLPLAYERS");
		getAllPlayers.addActionListener(this);
		leftLayout.putConstraint(SpringLayout.NORTH, getAllPlayers, 15,
				SpringLayout.NORTH, leftPane);
		leftLayout.putConstraint(SpringLayout.WEST, getAllPlayers, 5,
				SpringLayout.WEST, leftPane);
		leftPane.add(getAllPlayers);

		playerList = new JList(new Vector<Player>(availablePlayers.values()));

		scrollPane = new JScrollPane(playerList);
		leftLayout.putConstraint(SpringLayout.NORTH, scrollPane, 5,
				SpringLayout.SOUTH, getAllPlayers);
		leftLayout.putConstraint(SpringLayout.WEST, scrollPane, 5,
				SpringLayout.WEST, leftPane);
		leftLayout.putConstraint(SpringLayout.EAST, scrollPane, -5,
				SpringLayout.EAST, leftPane);
		leftLayout.putConstraint(SpringLayout.SOUTH, scrollPane, -30,
				SpringLayout.SOUTH, leftPane);
		leftPane.add(scrollPane);

		addPlayerToTeam = new JButton("Add Player");
		addPlayerToTeam.setName("ADDPLAYER");
		addPlayerToTeam.addActionListener(this);
		leftLayout.putConstraint(SpringLayout.NORTH, addPlayerToTeam, 5,
				SpringLayout.SOUTH, scrollPane);
		leftLayout.putConstraint(SpringLayout.WEST, addPlayerToTeam, 5,
				SpringLayout.WEST, leftPane);
		leftPane.add(addPlayerToTeam);

		viewStats = new JButton("View Player Stats");
		viewStats.setName("VIEWSTATS");
		viewStats.addActionListener(this);
		leftLayout.putConstraint(SpringLayout.NORTH, viewStats, 5,
				SpringLayout.SOUTH, scrollPane);
		leftLayout.putConstraint(SpringLayout.WEST, viewStats, 5,
				SpringLayout.EAST, addPlayerToTeam);
		leftPane.add(viewStats);

		rightPane = new JPanel();
		rightPane.setLayout(rightLayout);

		JLabel connectionLabel = new JLabel("Connection Settings:");
		rightLayout.putConstraint(SpringLayout.NORTH, connectionLabel, 15,
				SpringLayout.NORTH, rightPane);
		rightLayout.putConstraint(SpringLayout.WEST, connectionLabel, 10,
				SpringLayout.WEST, rightPane);
		rightPane.add(connectionLabel);

		ipAndPort = new JTextField("127.0.0.1:7777");
		rightLayout.putConstraint(SpringLayout.NORTH, ipAndPort, 10,
				SpringLayout.NORTH, rightPane);
		rightLayout.putConstraint(SpringLayout.WEST, ipAndPort, 5,
				SpringLayout.EAST, connectionLabel);
		SpringLayout.Constraints constr = rightLayout.getConstraints(ipAndPort);
		constr.setWidth(Spring.constant(150));
		constr.setHeight(Spring.constant(25));
		rightPane.add(ipAndPort);

		clearTeam = new JButton("Clear Team");
		clearTeam.addActionListener(this);
		clearTeam.setName("CLEARTEAM");

		rightLayout.putConstraint(SpringLayout.NORTH, clearTeam, 15,
				SpringLayout.SOUTH, connectionLabel);
		rightLayout.putConstraint(SpringLayout.WEST, clearTeam, 10,
				SpringLayout.WEST, rightPane);
		rightPane.add(clearTeam);

		JLabel teamLabel = new JLabel("My Team:");
		rightLayout.putConstraint(SpringLayout.NORTH, teamLabel, 19,
				SpringLayout.SOUTH, connectionLabel);
		rightLayout.putConstraint(SpringLayout.WEST, teamLabel, 15,
				SpringLayout.EAST, clearTeam);
		rightPane.add(teamLabel);

		teamName = new JTextField("");
		rightLayout.putConstraint(SpringLayout.NORTH, teamName, 15,
				SpringLayout.SOUTH, connectionLabel);
		rightLayout.putConstraint(SpringLayout.WEST, teamName, 15,
				SpringLayout.EAST, teamLabel);
		constr = rightLayout.getConstraints(teamName);
		constr.setWidth(Spring.constant(150));
		constr.setHeight(Spring.constant(25));
		teamName.setName("NAME");
		teamName.addActionListener(this);

		rightPane.add(teamName);

		tModel = new DefaultTableModel(new String[] { "Player", "Position",
				"Stats", "" }, 0) {
			private static final long serialVersionUID = -7966810803471536930L;

			@Override
			public Class<?> getColumnClass(int columnIndex) {
				if (columnIndex == 3) {
					return JButton.class;
				} else {
					return String.class;
				}
			}

		};
		team = new JTable(tModel) {
			private static final long serialVersionUID = -2318718815921272126L;

			@Override
			public Class<?> getColumnClass(int col) {
				if (col == 3) {
					return JButton.class;
				} else {
					return String.class;
				}
			}

			@Override
			public boolean isCellEditable(int row, int col) {
				return false;
			}
		};
		team.getTableHeader().setBackground(new Color(173, 231, 234));
		team.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
		team.getColumnModel().getColumn(0).setMinWidth(100);
		team.getColumnModel().getColumn(1).setMinWidth(75);
		team.getColumnModel().getColumn(1).setMinWidth(100);
		team.getColumnModel().getColumn(2).setMinWidth(250);
		team.getColumnModel().getColumn(3)
				.setCellRenderer(new ButtonRenderer());
		team.addMouseListener(new JTableButtonMouseListener(team));
		JScrollPane tScroll = new JScrollPane(team);
		rightLayout.putConstraint(SpringLayout.NORTH, tScroll, 10,
				SpringLayout.SOUTH, clearTeam);
		rightLayout.putConstraint(SpringLayout.WEST, tScroll, 10,
				SpringLayout.WEST, rightPane);
		rightLayout.putConstraint(SpringLayout.EAST, tScroll, -10,
				SpringLayout.EAST, rightPane);
		rightLayout.putConstraint(SpringLayout.SOUTH, tScroll, -10,
				SpringLayout.SOUTH, rightPane);
		rightPane.add(tScroll);

		contentPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, leftPane,
				rightPane);

		setPreferredSize(new Dimension(950, 475));
		setMinimumSize(new Dimension(950, 475));

		contentPane.setDividerLocation(300);

		notifyThread = new NotifyThread();
		notifyThread.start();

		teamNotifyThread = new NotifyThreadTeam();
		teamNotifyThread.start();

		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		setContentPane(contentPane);
		setVisible(true);

		updateTeam();
	}

	private void updateTeam() {
		List<Player> team = new ArrayList<Player>(
				FantasyClient.currentTeam.values());
		Collections.sort(team, new Comparator<Player>() {

			@Override
			public int compare(Player o1, Player o2) {
				if (o1.getPosition().ordinal() < o2.getPosition().ordinal()) {
					return -1;
				} else if (o1.getPosition().ordinal() > o2.getPosition()
						.ordinal()) {
					return 1;
				} else {
					return o1.getName().compareTo(o2.getName());
				}
			}

		});

		JButton update = new JButton("Update") {
			private static final long serialVersionUID = 1385497003780695007L;

			public String toString() {
				return "Update";
			}
		};
		update.setActionCommand("UPDATE");
		update.addActionListener(this);

		for (int row = 0; row < team.size(); row++) {
			if (row >= tModel.getRowCount()) {
				tModel.addRow(new Object[] {});
			}

			tModel.setValueAt(team.get(row).getName(), row, 0);
			tModel.setValueAt(team.get(row).getPosition(), row, 1);
			tModel.setValueAt(statsToString(team.get(row).getStats()), row, 2);
			tModel.setValueAt(update, row, 3);
		}
	}

	private String statsToString(HashMap<String, Integer> stats) {
		StringBuilder sb = new StringBuilder();

		for (Entry<String, Integer> e : stats.entrySet()) {
			sb.append(e.getKey() + " - " + e.getValue() + ", ");
		}

		return sb.toString().substring(0, sb.length() - 2);
	}

	public static void addPlayers(List<Player> allPlayers) {
		synchronized (availablePlayers) {
			for (Player player : allPlayers) {
				availablePlayers.put(player.getName(), player);
			}
			availablePlayers.notify();
		}
	}

	public static void updatePlayer(List<Player> players) {
		synchronized (currentTeam) {
			for (Player player : players) {
				currentTeam.put(player.getName(), player);
			}
			currentTeam.notify();
		}
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		String source = ((Component) e.getSource()).getName();

		if (source == null) {
			return;
		}

		if (source.equals("ADDPLAYER")) {
			Player toAdd = (Player) playerList.getSelectedValue();
			if (toAdd != null) {
				currentTeam.put(toAdd.getName(), toAdd);
				updateTeam();
			}
		} else if (source.equals("VIEWSTATS")) {
			StatDialog dialog = new StatDialog(
					(Player) playerList.getSelectedValue());
			dialog.setModal(true);
			dialog.setLocationRelativeTo(leftPane);
			dialog.setVisible(true);
		} else if (source.equals("GETALLPLAYERS")) {
			int port = 0;
			GetAllPlayers serv = new GetAllPlayers();
			String[] split = ipAndPort.getText().split(":");
			if (split.length != 2) {
				ipAndPort.setText("INVALID");
			}
			try {
				port = Integer.parseInt(split[1].trim());
			} catch (NumberFormatException e1) {
				ipAndPort.setText("INVALID");
			}
			serv.execute(split[0].trim(), port);
		} else if (source.equals("CLEARTEAM")) {
			currentTeam.clear();
			teamName.setEnabled(true);
			updateTeam();
		} else if (source.equals("NAME")) {
			teamName.setEnabled(false);
		}

	}

	public static void main(String[] args) {
		SwingUtilities.invokeLater(new Runnable() {

			@Override
			public void run() {
				new FantasyClient();
			}
		});
	}

	private class NotifyThread extends Thread {

		private boolean quit = false;

		public NotifyThread() {
		}

		@Override
		public synchronized void run() {
			while (!quit) {
				try {
					synchronized (availablePlayers) {
						availablePlayers.wait();

						playerList.setListData(new Vector<Player>(
								availablePlayers.values()));
						scrollPane.revalidate();
						leftPane.repaint();
					}
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}

		public void quit() {
			quit = true;
			availablePlayers.notify();
		}
	}

	private class NotifyThreadTeam extends Thread {

		private boolean quit = false;

		public NotifyThreadTeam() {
		}

		@Override
		public synchronized void run() {
			while (!quit) {
				try {
					synchronized (currentTeam) {
						currentTeam.wait();

						updateTeam();
					}
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}

		public void quit() {
			quit = true;
			currentTeam.notify();
		}
	}

	public class JTableButtonMouseListener extends MouseAdapter {
		private final JTable table;

		public JTableButtonMouseListener(JTable table) {
			this.table = table;
		}

		@Override
		public void mouseClicked(MouseEvent e) {
			int column = table.getColumnModel().getColumnIndexAtX(e.getX());
			int row = e.getY() / table.getRowHeight();

			if (row < table.getRowCount() && row >= 0
					&& column < table.getColumnCount() && column >= 0) {
				Object value = table.getValueAt(row, column);

				if (value instanceof JButton) {
					int port = 0;
					GetPlayer serv = new GetPlayer();
					String[] connectionSettings = ipAndPort.getText()
							.split(":");
					if (connectionSettings.length != 2) {
						ipAndPort.setText("INVALID");
					}
					try {
						port = Integer.parseInt(connectionSettings[1].trim());
					} catch (NumberFormatException e1) {
						ipAndPort.setText("INVALID");
					}
					String name = (String) tModel.getValueAt(
							team.getSelectedRow(), 0);
					serv.execute(connectionSettings[0].trim(), port, name);
				}

			}
		}
	}

	private class ButtonRenderer extends JButton implements TableCellRenderer {
		private static final long serialVersionUID = 3353686363014556645L;

		public ButtonRenderer() {
			setOpaque(true);
		}

		public Component getTableCellRendererComponent(JTable table,
				Object value, boolean isSelected, boolean hasFocus, int row,
				int column) {
			if (isSelected) {
				setForeground(table.getSelectionForeground());
				setBackground(table.getSelectionBackground());
			} else {
				setForeground(table.getForeground());
				setBackground(UIManager.getColor("Button.background"));
			}
			setText((value == null) ? "" : value.toString());
			return this;
		}
	}
}
